package com.xquant.pricing.calc.controller;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.xquant.common.core.domain.Underlying;
import com.xquant.common.engine.xPP.cxf.NotifyDataFacade;
import com.xquant.common.engine.xPP.cxf.XQuantPPService;
import com.xquant.common.engine.xPP.request.CalcEngineParam;
import com.xquant.common.engine.xPP.request.CalcPricingParam;
import com.xquant.common.engine.xPP.request.CalcSceneParam;
import com.xquant.common.engine.xPP.request.scene.CalcSceneQuoteEngineParam;
import com.xquant.common.engine.xPP.request.scene.Factor;
import com.xquant.common.engine.xPP.request.scene.SceneCalcEngineParam;
import com.xquant.common.engine.xPP.response.CalcMarket;
import com.xquant.common.engine.xPP.response.CalcScene;
import com.xquant.common.engine.xPP.response.Contract;
import com.xquant.common.engine.xPP.response.SceneResponse;
import com.xquant.common.security.utils.SecurityUtils;
import com.xquant.pricing.calc.entity.CalcEngineParam4Simple;
import com.xquant.pricing.calc.entity.ProductTypeEnum;
import com.xquant.pricing.calc.entity.RequestCodeEnum;
import com.xquant.pricing.calc.entity.ResponseVo;
import com.xquant.pricing.calc.service.CalcCommonService;
import com.xquant.pricing.calc.service.CalcEngineService;
import com.xquant.pricing.calc.service.CalcLogsService;
import com.xquant.pricing.calc.service.impl.otcOption.CalcCommonService4OtcOptionSplitService;
import com.xquant.pricing.calc.service.impl.otcOption.split.CalcCommonService4OtcOptionSplit;
import com.xquant.pricing.pricingConfig.mapper.CalendarMapper;
import com.xquant.pricing.sysconfig.service.ISystemConfigService;
import com.xquant.pricing.systerm.service.InstrumentService;
import com.xquant.pricing.utils.*;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * 功能说明：计算服务调用统一入口抽象类
 * 编写作者：zhaoes
 * 开发日期：2021-10-20 09:35:00
 * 修改记录：修改日期   修改人    修改内容
 */
public abstract class CalcBaseController {

    public abstract String getProductType();

    @Autowired
    private NotifyDataFacade notifyDataFacade;

    public boolean getOpenLog() {
        return false;
    }

    protected static final Logger logger = LoggerFactory.getLogger(CalcBaseController.class);

    @Autowired
    protected CalcEngineService calcEngineService;
    @Autowired
    protected CalcLogsService calcLogsService;
    @Autowired
    protected XQuantPPService ppService;
    @Autowired
    private CalcCommonService4OtcOptionSplitService calcCommonService4OtcOptionSplitService;
    @Autowired
    private CalendarMapper calendarMapper;

    @Autowired
    private InstrumentService instrumentService;
    @Autowired
    private ISystemConfigService systemConfigService;

    /**
     * 根据开始日和结束日 获取交易日
     * param:  {
     * startDay:'YYYY-MM-DD',
     * endDay:'YYYY-MM-DD',
     * offset:'number'
     * }
     */
    @RequestMapping(value = "calcMarket/getCalendar.action")
    public ResponseVo getCalendarList(@RequestBody Map<String, Object> query) {
        List<Map<String, Object>> calendarList = calendarMapper.QueryCalendar(query);
        for (Map<String, Object> stringObjectMap : calendarList) {
            if (stringObjectMap.get("CAL_FLAG").equals("1")) {
                return ResponseVo.success(true);
            }
        }
        return ResponseVo.success(calendarList);
    }

    @RequestMapping(value = "calcMarket/getTerminationPaymentDate.action")
    public ResponseVo getTerminationPaymentDate(@RequestBody Map<String, Object> query) {
        query.put("calCode", "CHINA_EX");
        Map<String, Object> calDay = calendarMapper.getTerminationPaymentDate(query);
        if (null == calDay) {
            calDay = new HashMap<>();
            calDay.put("CAL_DAY", query.getOrDefault("calDay", ""));
        }
        return ResponseVo.success(calDay);
    }

    /**
     * 获取交易日上一个 Preceding
     *
     * @param query
     * @return
     */
    @RequestMapping(value = "calcMarket/getFixDate.action")
    public ResponseVo getFixDate(@RequestBody Map<String, Object> query) {
        query.put("calCode", "CHINA_EX");
        Map<String, Object> calDay = calendarMapper.getFixDate(query);
        if (null == calDay) {
            calDay = new HashMap<>();
            calDay.put("CAL_DAY", query.getOrDefault("calDay", ""));
        }
        return ResponseVo.success(calDay);
    }

    @RequestMapping(value = "calcMarket/getOffset.action")
    public ResponseVo getOffset(@RequestBody Map<String, Object> query) {
        query.put("calCode", "CHINA_EX");
        Map<String, Object> calDay = calendarMapper.getOffset(query);
        return ResponseVo.success(calDay);
    }


    @RequestMapping(value = "/calcMarket/getCalcPricingParamByCfgId.action")
    public ResponseVo getCalcPricingParamByCfgId4CalcMarket(@RequestBody CalcEngineParam param) {
        CalcEngineParam engineParam = getCalcPricingParamByCfgId(param, RequestCodeEnum.CALC_MARKET.getCode());
        return ResponseVo.success(engineParam);
    }


    @RequestMapping(value = "/calcMarket.action")
    public Object calcMarket(@RequestBody CalcEngineParam param) {
        // 业务系统请求计算
        ContextHolder.getParam().put("uId", SecurityUtils.getUserId());
        ContextHolder.getParam().put("calcType", RequestCodeEnum.CALC_MARKET.getCode());
        ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("productType", getProductType());
        return doCalcMarket(param);
    }

    @PostMapping(value = "/calcSimpleMarket.action")
    @ResponseBody
    public Object calcSimpleMarket(@RequestBody CalcEngineParam4Simple param) {
        //return doCalcMarket(param);
        CalcCommonService4OtcOptionSplit delegateService = calcCommonService4OtcOptionSplitService.getDelegateService(param.getRecord().getOptionTrade().getOptionLegs().get(0).getProductType());
        CalcEngineParam split = delegateService.split(param);
        return doCalcMarket(split);
    }

    protected ResponseVo doCalcMarket(CalcEngineParam param) {
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        long start = System.currentTimeMillis();
        ContextHolder.getParam().put("reqTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
        ResponseVo rvo = new ResponseVo();
        try {
            /*对CalcInstrumentForm不做处理*/
          /*  if (SysUtils.isVoid(param.getCalcInstrumentForm().getProductType() != null)) {
                return ResponseVo.failure("productType不能为空");
            }
            ContextHolder.getParam().put("initOptionType", param.getCalcInstrumentForm().getInitProductType());
            ContextHolder.getParam().put("optionType", param.getCalcInstrumentForm().getProductType());
            */
            ContextHolder.getParam().put("initOptionType", StrUtil.isNotBlank(param.getInstrument().getInitProductType()) ? param.getInstrument().getInitProductType() : param.getInstrument().getProductType());
            ContextHolder.getParam().put("optionType", StrUtil.isNotBlank(param.getInstrument().getInitProductType()) ? param.getInstrument().getInitProductType() : param.getInstrument().getProductType());


            String instrument = String.valueOf(((List) param.getInstrument().getTermsheet().get("underlyerIds")).get(0));
//            String instrument = param.getCalcInstrumentForm().getInstrumentId();
            ContextHolder.getParam().put("instrument", instrument);

            CalcEngineParam calcEngineParam = new CalcEngineParam();
            calcEngineParam.setCalcPricingParam(param.getCalcPricingParam());
            calcEngineParam.setInstrument(param.getInstrument());
            calcEngineParam.setUnderlyingMarkets(param.getUnderlyingMarkets());
            calcEngineParam.setCustomPricingParam(param.getCustomPricingParam());
            ContextHolder.getParam().put("pCalc", JSONUtil.toJsonStr(calcEngineParam));
            ContextHolder.getParam().put("pCalcInstrument", JSONUtil.toJsonStr(calcEngineParam.getInstrument()));
            ContextHolder.getParam().put("pCalcPricing", JSONUtil.toJsonStr(calcEngineParam.getCalcPricingParam()));
            ContextHolder.getParam().put("valueDate", calcEngineParam.getCalcPricingParam().getValueDate());
            rvo = calcEngineService.calcEngine(getProductType(), RequestCodeEnum.CALC_MARKET.getCode(), calcEngineParam);
            if (SysUtils.isNotVoid(instrument)) {
                ContextHolder.getParam().put("iCode", instrument.split(",")[0]);
                ContextHolder.getParam().put("aType", instrument.split(",")[1]);
                ContextHolder.getParam().put("mType", instrument.split(",")[2]);
            }
        } catch (Exception e) {
            e.printStackTrace();
            rvo.setErrorCode(-1);
            rvo.setErrorMsg(e.getMessage());
        } finally {
            // 返回业务系统计算结果
            ContextHolder.getParam().put("status", rvo.getErrorCode() >= 0 ? 1 : 0);
            String pResponse = JSON.toJSONString(rvo);
            ContextHolder.getParam().put("pResponse", pResponse);
            ContextHolder.getParam().put("resTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
            long end = System.currentTimeMillis();
            if (rvo.getErrorCode() < 0) {

                logger.debug(MessageFormat.format("====定价平台总耗时:{0}ms====金融工具ID:{1}====获取stub耗时:{2}ns====计算耗时:{3}ns====startTime:{4}====endTime:{5}====upReq:{6}====upResp:{7}=====地址:{8}",
                        end - start,
                        param.getInstrument().getId(),
                        MapUtils.getString(ContextHolder.getParam(), "getStubTime"),
                        MapUtils.getString(ContextHolder.getParam(), "calcTime"),
                        start,
                        end,
                        MapUtils.getString(ContextHolder.getParam(), "pCalcInstrument"),
                        pResponse,
                        MapUtils.getString(ContextHolder.getParam(), "server"))
                );
            }
            /*9为不保存*/
            if (!(9 == param.getLogType())) {
                calcLogsService.saveReqAndResp(copyParams(ContextHolder.getParam()));
            }
        }
        return rvo;
    }

    /**
     * 反算波动率
     */
    protected ResponseVo calcMarketVolResult(CalcEngineParam param) {
        String isPrintLog = systemConfigService.selectConfigByKey("calcLog.isPrintLog");
        Long logTime = Long.valueOf(systemConfigService.selectConfigByKey("calcLog.greatTime"));
        String saveAllLog = systemConfigService.selectConfigByKey("calcLog.saveAllLog");

        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        String upReq = JSON.toJSONString(param);
        ContextHolder.getParam().put("upReq", upReq);
        long start = System.currentTimeMillis();
        List<Map<String, Object>> downparam = new ArrayList<>();
        ContextHolder.getParam().put("downparams", downparam);

        ContextHolder.getParam().put("upReqTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
        ResponseVo rvo = new ResponseVo();
        try {
            if (param.getInstrument().getTermsheet() != null && param.getInstrument().getTermsheet().get("customLegs") == null) {
                if (StringUtils.isBlank(param.getInstrument().getTermsheet().getString("productType"))) {
                    return ResponseVo.failure("productType不能为空");
                }
            } else {
                if (StringUtils.isBlank(param.getInstrument().getProductType())) {
                    return ResponseVo.failure("productType不能为空");
                }
            }
            CalcEngineParam calcEngineParam = new CalcEngineParam();
            calcEngineParam.setCalcPricingParam(param.getCalcPricingParam());
            calcEngineParam.setInstrument(param.getInstrument());
            calcEngineParam.setUnderlyingMarkets(param.getUnderlyingMarkets());
            calcEngineParam.setCustomPricingParam(param.getCustomPricingParam());
            ContextHolder.getParam().put("pCalc", JSONUtil.toJsonStr(calcEngineParam));
            ContextHolder.getParam().put("pCalcInstrument", JSONUtil.toJsonStr(calcEngineParam.getInstrument()));
            ContextHolder.getParam().put("pCalcPricing", JSONUtil.toJsonStr(calcEngineParam.getCalcPricingParam()));
            ContextHolder.getParam().put("valueDate", calcEngineParam.getCalcPricingParam().getValueDate());
            rvo = calcEngineService.calcEngineVol(getProductType(), RequestCodeEnum.CALC_MARKET.getCode(), calcEngineParam);
        } catch (Exception e) {
            e.printStackTrace();
            rvo.setErrorCode(-1);
            rvo.setErrorMsg(e.getMessage());
        } finally {
            // 返回业务系统计算结果
            ContextHolder.getParam().put("status", rvo.getErrorCode() >= 0 ? 1 : 0);
            String upResp = JSON.toJSONString(rvo);
            ContextHolder.getParam().put("upResp", upResp);
            ContextHolder.getParam().put("upRespTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
            long end = System.currentTimeMillis();
            if (rvo.getErrorCode() < 0 || ("1".equals(isPrintLog) && (end - start) > logTime)) {

                logger.debug(MessageFormat.format("====定价平台总耗时:{0}ms====金融工具ID:{1}====获取stub耗时:{2}ns====计算耗时:{3}ns====startTime:{4}====endTime:{5}====upReq:{6}====upResp:{7}=====地址:{8}",
                        end - start,
                        param.getInstrument().getId(),
                        MapUtils.getString(ContextHolder.getParam(), "getStubTime"),
                        MapUtils.getString(ContextHolder.getParam(), "calcTime"),
                        start,
                        end,
                        upReq,
                        upResp,
                        MapUtils.getString(ContextHolder.getParam(), "server"))
                );
            }
            calcLogsService.saveReqAndResp(copyParams(ContextHolder.getParam()));
        }
        return rvo;
    }

    @RequestMapping(value = "/calcScene.action")
    public Object calcScene(@RequestBody CalcEngineParam param) {
        {
            //1、通知行情变化
            notifyDataFacade.notifyDailySet();
            CacheUtil.getInstance().removeAllCacheData();
            CalcEngineParam calcEngineParam = new CalcEngineParam();
            //2、组装情景因子
            List<CalcSceneParam> calcSceneParamList = param.getCalcSceneParams();
            calcEngineParam.setCalcSceneParams(calcSceneParamList);
            CalcPricingParam calcPricingParam = new CalcPricingParam();
            calcPricingParam.setValueDate(DateUtils.getCurr_YYYY_MM_DD());
            calcPricingParam.setCfgId(SysUtils.toString(param.getCalcPricingParam().getCfgId()));
            calcEngineParam.setCalcPricingParam(calcPricingParam);
            // <挂钩标的+情景编号，<汇总指标+交易指标明细>>
            Map<String, Map> ret = new HashMap();
            // 标的列表
            List<String> uCodes = new ArrayList<>();
            Set<String> uCodesets = new HashSet<String>();
            List<Map> failList = new ArrayList();
            String instrumentId = String.valueOf(((List) param.getInstrument().getTermsheet().get("underlyerIds")).get(0));
            Map<String, Object> paramMap = new HashMap<>();
            paramMap.put("condition", String.valueOf(instrumentId).toUpperCase(Locale.ROOT));
            Underlying underlying = instrumentService.selectUnderlyingDatas(paramMap).get(0);
            //String strCalcEngineParam = JSON.toJSONString(calcEngineParam);
//                ExecutorService exec = Executors.newFixedThreadPool(50);
            Map failMap = new HashMap();

            //标的
            String uCode = instrumentId;

            try {
                // engineParam = calcCommonService4OtcOptionSplitService.beforeCalc(engineParam);
                ResponseVo retItem = calcEngineService.calcEngine(ProductTypeEnum.OTC_OPTION.getCode(), RequestCodeEnum.CALC_SCENE.getCode(), param);
                //ResponseVo retItem2 = calcEngineService.calcEngine(ProductTypeEnum.OTC_OPTION.getCode(), RequestCodeEnum.CALC_MARKET.getCode(), calcEngineParam);

                logger.info("计算结果:" + JSONUtil.parseObj(retItem));
                System.out.println("计算结果:" + JSONUtil.parseObj(retItem.getResult()));
                if (retItem.getResult() == null) {
                    failMap.put("iCode", underlying.getProductName());
                    failMap.put("uCode", uCode);
                    failMap.put("errInfo", "计算结果为空，请检查条款！");
                    failList.add(failMap);
                }
                if (retItem.getErrorCode() > 0) {
                    failMap.put("iCode", underlying.getProductName());
                    failMap.put("uCode", uCode);
                    failMap.put("errInfo", retItem.getErrorMsg());
                    failList.add(failMap);
                }

                List<CalcScene> calcScene = (List<CalcScene>) retItem.getResult();
                for (int K = 0; K < calcScene.size(); K++) {
                    CalcScene scene = calcScene.get(K);
                    Contract contract = scene.getContract();
                    BigDecimal spotPrice = new BigDecimal(contract.getSpotPrice());
                    String sceneID = calcScene.get(K).getSceneID();
                    String key = uCode + "#" + sceneID;
                    uCodesets.add(uCode);

                    if (!ret.containsKey(key)) {
                        Map initItem = new HashMap();
                        initItem.put("uName", underlying.getProductName());
                        initItem.put("uCode", uCode);
                        initItem.put("sceneID", sceneID);
                        initItem.put("uICode", uCode.split(",")[0]);
                        initItem.put("npv", BigDecimal.ZERO);
                        initItem.put("delta", BigDecimal.ZERO);
                        initItem.put("deltaCash", BigDecimal.ZERO);
                        initItem.put("gamma", BigDecimal.ZERO);
                        initItem.put("gammaCash", BigDecimal.ZERO);
                        initItem.put("vega", BigDecimal.ZERO);
                        initItem.put("rho", BigDecimal.ZERO);
                        initItem.put("theta", BigDecimal.ZERO);
                        initItem.put("calcPrice", contract.getSpotPrice());
                        initItem.put("details", new ArrayList<>());
                        ret.put(key, initItem);
                    }
                    boolean dwh = false;
                    BigDecimal npv = BigDecimal.ZERO;
                    BigDecimal delta = BigDecimal.ZERO;
                    BigDecimal deltaCash = BigDecimal.ZERO;
                    BigDecimal gamma = BigDecimal.ZERO;
                    BigDecimal gammaCash = BigDecimal.ZERO;
                    BigDecimal vega = BigDecimal.ZERO;
                    BigDecimal rho = BigDecimal.ZERO;
                    BigDecimal theta = BigDecimal.ZERO;
                    if (dwh) {
                        npv = new BigDecimal(contract.getNpv()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);
                        delta = new BigDecimal(contract.getDelta()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);
                        deltaCash = new BigDecimal(contract.getDelta()).multiply(param.getCalcInstrumentForm().getNotional()).multiply(spotPrice).setScale(4, BigDecimal.ROUND_UP);

                        gamma = new BigDecimal(contract.getGamma()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);
                        gammaCash = new BigDecimal(contract.getGamma()).multiply(param.getCalcInstrumentForm().getNotional()).multiply(spotPrice.multiply(spotPrice)).divide(new BigDecimal(100)).setScale(4, BigDecimal.ROUND_UP);

                        vega = new BigDecimal(contract.getVega()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);
                        rho = new BigDecimal(contract.getRho()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);
                        theta = new BigDecimal(contract.getTheta()).multiply(param.getCalcInstrumentForm().getNotional()).setScale(4, BigDecimal.ROUND_UP);

                    } else {
                        npv = new BigDecimal(contract.getNpv()).setScale(4, BigDecimal.ROUND_UP);
                        delta = new BigDecimal(contract.getDelta()).setScale(4, BigDecimal.ROUND_UP);
                        deltaCash = new BigDecimal(contract.getDelta()).multiply(spotPrice).setScale(4, BigDecimal.ROUND_UP);

                        // monitorDetail.setCashDelta(Double.parseDouble(contract.getDelta()) * Double.parseDouble(contract.getSpotPrice()));
                        // monitorDetail.setCashGamma(Double.parseDouble(contract.getGamma()) * Double.parseDouble(contract.getSpotPrice()) * Double.parseDouble(contract.getSpotPrice()) / 100);

                        gamma = new BigDecimal(contract.getGamma()).setScale(4, BigDecimal.ROUND_UP);
                        gammaCash = new BigDecimal(contract.getGamma()).multiply(spotPrice.multiply(spotPrice)).divide(new BigDecimal(100)).setScale(4, BigDecimal.ROUND_UP);

                        vega = new BigDecimal(contract.getVega()).setScale(4, BigDecimal.ROUND_UP);
                        rho = new BigDecimal(contract.getRho()).setScale(4, BigDecimal.ROUND_UP);
                        theta = new BigDecimal(contract.getTheta()).setScale(4, BigDecimal.ROUND_UP);
                    }
                    String[] underlyId = param.getCalcInstrumentForm().getInstrumentId().split(",");
                    Map item = new HashMap();

                    item.put("iCode", underlyId[0]);
                    item.put("aType", underlyId[1]);
                    item.put("mType", underlyId[2]);
                    item.put("npv", npv);
                    item.put("delta", delta);
                    item.put("deltaCash", deltaCash);
                    item.put("gamma", gamma);
                    item.put("gammaCash", gammaCash);
                    item.put("vega", vega);
                    item.put("rho", rho);
                    item.put("theta", theta);
                    item.put("calcPrice", contract.getSpotPrice());
                    ret.get(key).put("npv", npv.add((BigDecimal) (ret.get(key).get("npv"))));
                    ret.get(key).put("delta", delta.add((BigDecimal) (ret.get(key).get("delta"))));
                    ret.get(key).put("deltaCash", deltaCash.add((BigDecimal) (ret.get(key).get("deltaCash"))));
                    ret.get(key).put("gamma", gamma.add((BigDecimal) (ret.get(key).get("gamma"))));
                    ret.get(key).put("gammaCash", gammaCash.add((BigDecimal) (ret.get(key).get("gammaCash"))));
                    ret.get(key).put("vega", vega.add((BigDecimal) (ret.get(key).get("vega"))));
                    ret.get(key).put("rho", rho.add((BigDecimal) (ret.get(key).get("rho"))));
                    ret.get(key).put("theta", theta.add((BigDecimal) (ret.get(key).get("theta"))));
                    for (int j = 0; j < calcSceneParamList.size(); j++) {
                        CalcSceneParam calcSceneParam = calcSceneParamList.get(j);
                        if (StrUtil.equals(calcSceneParam.getSceneID(), sceneID)) {
                            ret.get(key).put("volSurfaceInc", calcSceneParam.getUnderlyingSceneParams().get(0).getVolSurfaceInc() == null ? '1' : calcSceneParam.getUnderlyingSceneParams().get(0).getVolSurfaceInc());
                            ret.get(key).put("volSurfaceValInc", calcSceneParam.getUnderlyingSceneParams().get(0).getVolSurfaceInc() == null ? '0' : calcSceneParam.getUnderlyingSceneParams().get(0).getVolSurfaceInc().getValue());
                            ret.get(key).put("spotPriceInc", calcSceneParam.getUnderlyingSceneParams().get(0).getSpotPriceInc() == null ? '1' : calcSceneParam.getUnderlyingSceneParams().get(0).getSpotPriceInc());
                            ret.get(key).put("spotPriceValInc", calcSceneParam.getUnderlyingSceneParams().get(0).getSpotPriceInc() == null ? '0' : calcSceneParam.getUnderlyingSceneParams().get(0).getSpotPriceInc().getValue());
                        }
                    }
                    ret.get(key).put("volRate", scene.getContract().getVolRate());
                    //8个联合主键
                    ret.get(key).put("iCode", underlyId[0]);
                    ret.get(key).put("aType", underlyId[1]);
                    ret.get(key).put("mType", underlyId[2]);
                    ret.get(key).put("accId", null);
                    ret.get(key).put("dimAccExt", null);
                    ret.get(key).put("dimLs", null);
                    ret.get(key).put("currency", "CNY");
                    ret.get(key).put("dimSeq", null);

                    ((ArrayList) ret.get(key).get("details")).add(item);
                }
            } catch (Exception ex) {
                failMap.put("iCode", underlying.getProductName());
                failMap.put("uCode", uCode);
                failMap.put("errInfo", ex.getMessage());
                failList.add(failMap);
                ex.printStackTrace();
            }

            //处理计算结果
            Map<String, Map> resResult = new HashMap();
            Map<String, Map> realResult = new HashMap();
            uCodes.addAll(uCodesets);
            for (int i = 0; i < uCodes.size(); i++) {
                for (Map.Entry<String, Map> entry : ret.entrySet()) {

                    if (StrUtil.equals(entry.getKey().split("#")[0], uCodes.get(i))) {
                        Map valMap = entry.getValue();
                        String key = MapUtil.getStr(valMap, "uCode") + "#" + MapUtil.getStr(valMap, "volSurfaceValInc");
                        String spotPriceValInc = MapUtil.getStr(valMap, "spotPriceValInc");
                        String spotPriceValIncN = spotPriceValInc.replace(".", "_");
                        String volSurfaceValInc = MapUtil.getStr(valMap, "volSurfaceValInc");
                        String volSurfaceValIncN = volSurfaceValInc.replace(".", "_");
                        String calcPrice = MapUtil.getStr(valMap, "calcPrice");
                        String calcPriceN = calcPrice.replace(".", "_");
                        if (!resResult.containsKey(key)) {
                            Map initItem = new HashMap();
                            initItem.put("uName", MapUtil.getStr(valMap, "uName"));
                            initItem.put("uCode", MapUtil.getStr(valMap, "uCode"));
                            initItem.put("uICode", MapUtil.getStr(valMap, "uCode").split(",")[0]);
                            initItem.put("volSurfaceValInc", volSurfaceValInc);
                            initItem.put("spotPriceValInc#" + spotPriceValIncN, spotPriceValInc);
                            initItem.put("calcPrice#" + calcPriceN, calcPrice);
                            initItem.put("volRate", MapUtil.getStr(valMap, "volRate"));
                            resResult.put(key, initItem);
                        }
                        Map map = resResult.get(key);

                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#npv", MapUtil.getDouble(valMap, "npv"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#delta", MapUtil.getDouble(valMap, "delta"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#deltaCash", MapUtil.getDouble(valMap, "deltaCash"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#gamma", MapUtil.getDouble(valMap, "gamma"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#gammaCash", MapUtil.getDouble(valMap, "gammaCash"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#theta", MapUtil.getDouble(valMap, "theta"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#vega", MapUtil.getDouble(valMap, "vega"));
                        map.put("spotPriceValInc#" + calcPriceN + "#" + spotPriceValIncN + "#rho", MapUtil.getDouble(valMap, "rho"));
                        map.put("calcPrice#" + calcPriceN, calcPrice);

                    }
                }
                realResult.put(uCodes.get(i), resResult);
            }
            //map 转List
            List<Map> successList = new ArrayList();
            for (int i = 0; i < uCodes.size(); i++) {
                Map map = realResult.get(uCodes.get(i));
                List<Map> sonList = new ArrayList(map.values());
                successList.addAll(sonList);
            }

            // 获取动态 fields
            Set<String> fieldSets = new HashSet<String>();
            if (successList.size() > 0) {
                fieldSets = successList.get(0).keySet();
            }
            sortSet = new TreeSet<>();
            sortSet.addAll(fieldSets);

            HashMap<String, Object> result = new HashMap<>();
            //进行升序排列
            successList = successList.stream()
                    .sorted(Comparator.comparing(
                            h -> (Float.parseFloat(h.get("volSurfaceValInc").toString()))))
                    .collect(Collectors.toList());
            result.put("success", successList);
            result.put("error", failList);
            result.put("fields", sortSet);
            return new CommResponse(true, result);
        }
    }

    Set<String> sortSet = new TreeSet<String>(new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);//降序排列
        }
    });

    @RequestMapping(value = "/calcPaymentInfo/getCalcPricingParamByCfgId.action")
    public ResponseVo getCalcPricingParamByCfgId4CalcPaymentInfo(@RequestBody CalcEngineParam param) {
        CalcEngineParam engineParam = getCalcPricingParamByCfgId(param, RequestCodeEnum.CALC_PAYMENTINFO.getCode());
        return ResponseVo.success(engineParam);
    }

    @RequestMapping(value = "/calcPaymentInfo.action")
    public Object calcPaymentInfo(@RequestBody CalcEngineParam param) {
        return doNormalCalcEngine(param, RequestCodeEnum.CALC_PAYMENTINFO.getCode());
    }

    /**
     * 获取市场数据
     *
     * @param param
     * @param requestCode
     * @return
     */
    protected CalcEngineParam getCalcPricingParamByCfgId(CalcEngineParam param, String requestCode) {
        CacheUtil.getInstance().removeAllCacheData();
        CalcCommonService calcCommonService = calcEngineService.getDelegateService(getProductType(), requestCode);
        String cfgId = "";
        if (param.getCalcPricingParam() != null && param.getCalcPricingParam().getLegParams() != null) {
            List<CalcEngineParam> lst = param.split();
            CalcEngineParam ret = new CalcEngineParam();
            ret.setCalcPricingParam(new CalcPricingParam());
            ret.getCalcPricingParam().setLegParams(new ArrayList<>());
            for (int i = 0; i < lst.size(); i++) {
                CalcPricingParam legParam = lst.get(i).getCalcPricingParam();
                cfgId = calcCommonService.getDefaultCfgId(legParam == null ? "" : legParam.getCfgId());
                CalcEngineParam item = calcCommonService.getCalcPricingParamByCfgId(lst.get(i), cfgId);
                item.getCalcPricingParam().setLegId(lst.get(i).getCalcPricingParam().getLegId());
                ret.getCalcPricingParam().getLegParams().add(item.getCalcPricingParam());
            }
            return ret;
        } else {
            cfgId = calcCommonService.getDefaultCfgId(param.getCalcPricingParam() == null ? null : param.getCalcPricingParam().getCfgId());
        }
        CalcEngineParam calcEngineParam = calcCommonService.getCalcPricingParamByCfgId(param, cfgId);
        calcEngineParam.setInstrument(param.getInstrument());
        if (calcEngineParam.getCalcPricingParam() != null) {
            calcEngineParam.getCalcPricingParam().setCfgId(cfgId);
        }
        return calcEngineParam;
    }

    @RequestMapping(value = "/readFixingDates/getCalcPricingParamByCfgId.action")
    public ResponseVo getCalcPricingParamByCfgId4ReadFixingDates(@RequestBody CalcEngineParam param) {
        CalcEngineParam engineParam = getCalcPricingParamByCfgId(param, RequestCodeEnum.READ_FIXINGDATES.getCode());
        return ResponseVo.success(engineParam);
    }

    @RequestMapping(value = "/readFixingDates.action")
    public Object readFixingDates(@RequestBody CalcEngineParam param) {
        return doNormalCalcEngine(param, RequestCodeEnum.READ_FIXINGDATES.getCode());
    }

    /**
     * @param params{ uId,calcType,productType,upReq,upReqTime,status,upResp,upRespTime,
     *                downparam:[{downReq,downReqTime,downResp,downRespTime}]
     *                }
     * @return
     */
    protected Map copyParams(Map params) {
        Map map = new WeakHashMap();
        map.putAll(params);
        return map;
    }


    /**
     * @param param
     * @return
     */
    @RequestMapping(value = "calcSceneQuote.action")
    public Object calcSceneQuote(@RequestBody CalcSceneQuoteEngineParam param) {
        List<SceneCalcEngineParam> list = null;
        try {
            list = param.buildCalcEngineParam();
        } catch (IRBaseException e) {
            return ResponseVo.failure(e.getMessage());
        }
        // 业务系统请求计算
        Long uId = SecurityUtils.getUserId();

        List<SceneResponse> results = new ArrayList<>();

        ExecutorService exec = Executors.newFixedThreadPool(ppService.getThreads());
        for (int i = 0; i < list.size(); i++) {
            SceneCalcEngineParam sceneCalcEngineParam = list.get(i);
            exec.execute(() -> {
                try {
                    ContextHolder.reset();
                    ContextHolder.getParam().put("uId", uId);
                    ContextHolder.getParam().put("calcType", RequestCodeEnum.CALC_MARKET.getCode());
                    ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
                    ContextHolder.getParam().put("productType", getProductType());
                    ResponseVo rvo = doCalcMarket(sceneCalcEngineParam.getCalcEngineParam());
                    SceneResponse srp = new SceneResponse();
                    for (Factor factor : sceneCalcEngineParam.getFactors()) {
                        if (Factor.FACTOR_STRIKE.equals(factor.getFactor())) {
                            srp.setStrike(factor.getValue());
                        } else if (Factor.FACTOR_STRIKE_PERCENTAGE.equals(factor.getFactor())) {
                            srp.setStrikePercentage(factor.getValue());
                        } else {
                            srp.setTerm(factor.getValue());
                        }
                    }
                    srp.setResult((CalcMarket) rvo.getResult());
                    results.add(srp);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            });
        }
        return ResponseVo.success(results);
    }

    protected Object doNormalCalcEngine(CalcEngineParam param, String requestCode) {

        String isPrintLog = systemConfigService.selectConfigByKey("calcLog.isPrintLog");
        Long logTime = Long.valueOf(systemConfigService.selectConfigByKey("calcLog.greatTime"));
        String saveAllLog = systemConfigService.selectConfigByKey("calcLog.saveAllLog");

        // 业务系统请求计算
        ContextHolder.getParam().put("uId", SecurityUtils.getUserId());
        ContextHolder.getParam().put("calcType", requestCode);
        ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("productType", getProductType());
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        String upReq = JSON.toJSONString(param);
        ContextHolder.getParam().put("upReq", upReq);
        ContextHolder.getParam().put("upReqTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
        long start = System.currentTimeMillis();
//        logger.debug(MessageFormat.format("====time:{0}====upReq:{1}",System.currentTimeMillis(), upReq));
        List<Map<String, Object>> downparam = new ArrayList<>();
        ContextHolder.getParam().put("downparams", downparam);

        ResponseVo rvo = new ResponseVo();
        try {
            if (StringUtils.isBlank(param.getInstrument().getProductType())) {
                return ResponseVo.failure("productType不能为空");
            }
            rvo = calcEngineService.calcEngine(getProductType(), requestCode, param);
            return rvo;
        } catch (Exception e) {
            e.printStackTrace();
            rvo.setErrorCode(-1);
            rvo.setErrorMsg(e.getMessage());
            return rvo;
        } finally {
            // 返回业务系统计算结果
            ContextHolder.getParam().put("status", rvo.getErrorCode() >= 0 ? 1 : 0);
            String upResp = JSON.toJSONString(rvo);
            ContextHolder.getParam().put("upResp", upResp);
            long end = System.currentTimeMillis();

            if (rvo.getErrorCode() < 0 || ("1".equals(isPrintLog) && (end - start) > logTime)) {
                logger.debug(MessageFormat.format("====定价平台总耗时:{0}ms====金融工具ID:{1}====获取stub耗时:{2}ns====计算耗时:{3}ns====startTime:{4}====endTime:{5}====upReq:{6}====upResp:{7}=====地址:{8}",
                        end - start,
                        param.getInstrument().getId(),
                        MapUtils.getString(ContextHolder.getParam(), "getStubTime"),
                        MapUtils.getString(ContextHolder.getParam(), "calcTime"),
                        start,
                        end,
                        upReq,
                        upResp,
                        MapUtils.getString(ContextHolder.getParam(), "server"))
                );
            }
            if (rvo.getErrorCode() < 0 || "1".equals(param.getLocalLog()) || "1".equals(saveAllLog)) {
                ContextHolder.getParam().put("upRespTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
                calcLogsService.saveReqAndResp(copyParams(ContextHolder.getParam()));
            }
        }
    }

    public Object calcEngine4Trial(CalcEngineParam param, String requestCode) {

        String isPrintLog = systemConfigService.selectConfigByKey("calcLog.isPrintLog");
        Long logTime = Long.valueOf(systemConfigService.selectConfigByKey("calcLog.greatTime"));

        // 业务系统请求计算
        ContextHolder.getParam().put("uId", SecurityUtils.getUserId());
        ContextHolder.getParam().put("calcType", requestCode);
        ContextHolder.getParam().put("calcDate", DateUtils.getCurr_YYYY_MM_DD());
        ContextHolder.getParam().put("productType", getProductType());
        if (StringUtils.isBlank(param.getInstrument().getId())) {
            param.getInstrument().setId(System.currentTimeMillis() + "");
        }
        String upReq = JSON.toJSONString(param);
        ContextHolder.getParam().put("upReq", upReq);
        ContextHolder.getParam().put("upReqTime", DateUtils.getCurrentDateTimeWOnlyMilliS());
        long start = System.currentTimeMillis();
//        logger.debug(MessageFormat.format("====time:{0}====upReq:{1}",System.currentTimeMillis(), upReq));
        List<Map<String, Object>> downparam = new ArrayList<>();
        ContextHolder.getParam().put("downparams", downparam);

        ResponseVo rvo = new ResponseVo();
        try {
            if (StringUtils.isBlank(param.getInstrument().getProductType())) {
                return ResponseVo.failure("productType不能为空");
            }
            rvo = calcEngineService.calcEngine(getProductType(), requestCode, param);
            return rvo;
        } catch (Exception e) {
            e.printStackTrace();
            rvo.setErrorCode(-1);
            rvo.setErrorMsg(e.getMessage());
            return rvo;
        } finally {
            // 返回业务系统计算结果
            ContextHolder.getParam().put("status", rvo.getErrorCode() >= 0 ? 1 : 0);
            String upResp = JSON.toJSONString(rvo);
            ContextHolder.getParam().put("upResp", upResp);
            long end = System.currentTimeMillis();

            if (rvo.getErrorCode() < 0 || ("1".equals(isPrintLog) && (end - start) > logTime)) {
                logger.debug(MessageFormat.format("====定价平台总耗时:{0}ms====金融工具ID:{1}====获取stub耗时:{2}ns====计算耗时:{3}ns====startTime:{4}====endTime:{5}====upReq:{6}====upResp:{7}=====地址:{8}",
                        end - start,
                        param.getInstrument().getId(),
                        MapUtils.getString(ContextHolder.getParam(), "getStubTime"),
                        MapUtils.getString(ContextHolder.getParam(), "calcTime"),
                        start,
                        end,
                        upReq,
                        upResp,
                        MapUtils.getString(ContextHolder.getParam(), "server"))
                );
            }
        }
    }
}
